package com.superiron.scaffold.core.service.secure.support;

import com.superiron.scaffold.core.exception.ApiException;
import com.superiron.scaffold.core.service.secure.service.DataAuthorityDetectorService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;

/**
 * 数据权限拦截校验
 *
 * @author yzg
 * @date 2022年7月8日11:50:16
 */
@Slf4j
@Service
@Aspect
public class ApiDataAuthorityDetectAdvice{
	
	@Autowired
	private DataAuthorityDetectorService dataAuthorityDetectorService;
	
	/**
	 * 切点设置为带有@APIDataAuthorities的方法
	 */
	@Pointcut("@annotation(com.tsit.iot.core.common.secure.support.APIDataAuthorities)")
	public void pointAPIDataAuthorities(){}
	
	/**
	 * 切点设置为带有@APIDataAuthority的方法
	 */
	@Pointcut("@annotation(com.tsit.iot.core.common.secure.support.APIDataAuthority)")
	public void validAPIDataAuthority(){}
	
	@Before("pointAPIDataAuthorities()")
	public void beforeAPIDataAuthorities(JoinPoint point){
		MethodSignature methodSignature = (MethodSignature)point.getSignature() ;
		Method method = methodSignature.getMethod() ;
		APIDataAuthorities authorityMethod = method.getAnnotation(APIDataAuthorities.class) ;
		if(!authorityMethod.enabled()){
			return ;
		}
		APIDataAuthority[] apiDataAuthorities ;
		if(authorityMethod.value() != null && authorityMethod.value().length > 0){
			apiDataAuthorities = authorityMethod.value() ;
		}else{
			//获取类上APIDataAuthority注解
			apiDataAuthorities = clazzAnnotion(point) ;
		}
		doValid( point , apiDataAuthorities ) ;
	}
	
	@Before("validAPIDataAuthority()")
	public void beforeAPIDataAuthority(JoinPoint point){
		MethodSignature methodSignature = (MethodSignature)point.getSignature() ;
		Method method = methodSignature.getMethod() ;
		APIDataAuthority authorityMethod = method.getAnnotation(APIDataAuthority.class) ;
		if(!authorityMethod.enabled()){
			return ;
		}
		APIDataAuthority[] apiDataAuthorities = new APIDataAuthority[]{authorityMethod} ;
		Class clazz = authorityMethod.value() ;
		String detectorName = clazz.getName() ;
		//如果是默认值的直接查询类注解
		if(DataAuthorityDetector.class.getName().equals(detectorName)){
			//获取类上APIDataAuthority注解
			apiDataAuthorities = clazzAnnotion(point) ;
		}
		doValid( point , apiDataAuthorities ) ;
	}
	
	/**
	 * 校验权限
	 * @param point
	 * @param apiDataAuthorities
	 */
	private void doValid(JoinPoint point, APIDataAuthority[] apiDataAuthorities) {
		if(apiDataAuthorities == null || apiDataAuthorities.length == 0){
			return ;
		}
		MethodSignature methodSignature = (MethodSignature)point.getSignature() ;
		String[] methodParaNames = methodSignature.getParameterNames() ;
		Object[] methodParas = point.getArgs() ;
		boolean result ;
		for(APIDataAuthority authority : apiDataAuthorities){
			Object[] params = detectParam(methodParaNames, methodParas, authority);
			if(params == null){
				log.error("获取参数不存在，直接跳过：{} {} "  , authority.value() , authority.params());
				continue;
			}
			Class clazz = authority.value() ;
			String detectorName = clazz.getName() ;
			//如果是默认值的直接跳过 不进行检测
			if(DataAuthorityDetector.class.getName().equals(detectorName)){
				log.error("检测器不存在 直接跳过：{} {} "  , authority.value() , authority.params());
				continue;
			}
			result = dataAuthorityDetectorService.detect(detectorName , params) ;
			if(!result){
				throw new ApiDataAuthorityValidateException() ;
			}
		}
	}
	
	/**
	 * 获取类上注解
	 * @param point
	 * @return
	 */
	private APIDataAuthority[] clazzAnnotion(JoinPoint point){
		APIDataAuthority apiDataAuthority =  point.getTarget().getClass().getAnnotation(APIDataAuthority.class) ;
		APIDataAuthorities apiDataAuthorities =  point.getTarget().getClass().getAnnotation(APIDataAuthorities.class) ;
		if(apiDataAuthorities != null){
			return apiDataAuthorities.value() ;
		}
		return new APIDataAuthority[]{apiDataAuthority};
	}
	
	
	/**
	 * 提取参数
	 * @param paraNames
	 * @param arguments
	 * @param authority
	 * @return
	 */
	private Object[] detectParam(String[] paraNames, Object[] arguments, APIDataAuthority authority) {
		String[] paramsAuth = authority.params() ;
		Object[] detectParams = new Object[paramsAuth.length];
		for (int i = 0 ; i < paramsAuth.length ; i++){
			//提取参数
			String paramAuthParam = paramsAuth[i] ;
			String paramAuthName = paramsAuth[i] ;
			if(paramAuthParam.indexOf(".") > -1){
				if( paramAuthParam.split("\\.").length > 2
						|| ".".equals(paramAuthParam.charAt(0))
						|| ".".equals(paramAuthParam.charAt(paramAuthParam.length() -1))){
					log.error("DataAuthorityDetector 参数配置错误，最多支持二级深度{} " , paramAuthParam);
					throw new ApiException("系统错误") ;
				}
				paramAuthName = paramAuthParam.split("\\.")[0] ;
			}
			
			
			int paraIdx = -1 ;
			for(int j = 0 ; j < paraNames.length ; j++){
				if(StringUtils.equals(paramAuthName , paraNames[j])){
					paraIdx = j ;
					break;
				}
			}
			if(paraIdx == -1){
				log.error("DataAuthorityDetector 参数不存在， " , paramAuthName);
				return null ;
			}
			Object param = arguments[paraIdx] ;
			Object obj = arguments[paraIdx] ;
			if(paramAuthParam.indexOf(".") > -1){
				try {
					param = PropertyUtils.getProperty(obj , paramAuthParam.split("\\.")[1]) ;
				} catch (Exception e) {
					log.error("获取对象属性错误 {} {} error:{} " , obj.getClass().getName() , paramAuthParam.split("\\.")[1] , e);
					throw new ApiException("系统错误") ;
				}
			}
			detectParams[i] = param ;
		}
		return detectParams ;
	}
	
	
}
